//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public License
// along with this program.  If not, see http://www.gnu.org/licenses/.
// 

#include <cassert>

#include <FlowMultiplexer.h>

Define_Module(FlowMultiplexer);

FlowMultiplexer::~FlowMultiplexer() {
    // TODO Auto-generated destructor stub
}

void FlowMultiplexer::initialize(int stage) {

    BaseModule::initialize(stage);

    if(stage == 0) {


        lowLayerNumber = par("numOut");
        upLayerNumber = par("numIn");

        nicToUse = par("nicToUse");

        if(upLayerNumber > 0) {
            toUpLayer = new int[upLayerNumber];
            fromUpLayer = new int[upLayerNumber];
            toControlUpLayer = new int[upLayerNumber];
            fromControlUpLayer = new int[upLayerNumber];
        }
        else {
            toUpLayer = NULL;
            fromUpLayer = NULL;
            toControlUpLayer = NULL;
            fromControlUpLayer = NULL;
        }

        for(int i = 0; i < upLayerNumber; i++) {
            toUpLayer[i] = findGate("upperMultiLayerOut", i);
            fromUpLayer[i] = findGate("upperMultiLayerIn", i);
            toControlUpLayer[i] = findGate("upperMultiControlOut", i);
            fromControlUpLayer[i] = findGate("upperMultiControlIn", i);
        }


        if(lowLayerNumber > 0) {
            toDownLayer = new int[lowLayerNumber];
            fromDownLayer = new int[lowLayerNumber];
            toControlDownLayer = new int[lowLayerNumber];
            fromControlDownLayer = new int[lowLayerNumber];
        }
        else {
            toDownLayer = NULL;
            fromDownLayer = NULL;
            toControlDownLayer = NULL;
            fromControlDownLayer = NULL;
        }

        for(int i = 0; i < lowLayerNumber; i++) {
            toDownLayer[i] = findGate("lowerMultiLayerOut", i);
            fromDownLayer[i] = findGate("lowerMultiLayerIn", i);
            toControlDownLayer[i] = findGate("lowerMultiControlOut", i);
            fromControlDownLayer[i] = findGate("lowerMultiControlIn", i);
        }
    }
}


void FlowMultiplexer::handleMessage(cMessage *msg) {
    if (msg->isSelfMessage()){
        handleSelfMsg(msg);
    }/* else if(msg->getArrivalGateId()==fromUpLayer) {
        //recordPacket(PassedMessage::INCOMING,PassedMessage::UPPER_DATA,msg);
        handleUpperMsg(msg);
    } else if(msg->getArrivalGateId()==fromControlUpLayer) {
        //recordPacket(PassedMessage::INCOMING,PassedMessage::UPPER_CONTROL,msg);
        handleUpperControl(msg);
    }*/
    else{
        bool gateFound = false;

        for(int i = 0; i < lowLayerNumber; i++) {
            if(msg->getArrivalGateId()==fromControlDownLayer[i]){
                //recordPacket(PassedMessage::INCOMING,PassedMessage::LOWER_CONTROL,msg);
                handleLowerControl(msg, i);
                gateFound = true;
            }
            else if(msg->getArrivalGateId()==fromDownLayer[i]) {
                //recordPacket(PassedMessage::INCOMING,PassedMessage::LOWER_DATA,msg);
                handleLowerMsg(msg, i);
                gateFound = true;
            }
        }

        for(int i = 0; i < upLayerNumber; i++) {
            if(msg->getArrivalGateId()==fromControlUpLayer[i]){
                //recordPacket(PassedMessage::INCOMING,PassedMessage::LOWER_CONTROL,msg);
                handleUpperControl(msg, i);
                gateFound = true;
            }
            else if(msg->getArrivalGateId()==fromUpLayer[i]) {
                //recordPacket(PassedMessage::INCOMING,PassedMessage::LOWER_DATA,msg);
                handleUpperMsg(msg, i);
                gateFound = true;
            }
        }

        if(!gateFound) {
            if(msg->getArrivalGateId()==-1) {
                /* Classes extending this class may not use all the gates, f.e.
                 * BaseApplLayer has no upper gates. In this case all upper gate-
                 * handles are initialized to -1. When getArrivalGateId() equals -1,
                 * it would be wrong to forward the message to one of these gates,
                 * as they actually don't exist, so raise an error instead.
                 */
                opp_error("No self message and no gateID?? Check configuration.");
            } else {
                /* msg->getArrivalGateId() should be valid, but it isn't recognized
                 * here. This could signal the case that this class is extended
                 * with extra gates, but handleMessage() isn't overridden to
                 * check for the new gate(s).
                 */
                opp_error("Unknown gateID?? Check configuration or override handleMessage().");
            }
        }
    }
}

void FlowMultiplexer::handleLowerMsg(cMessage *msg, int nicIndex) {
    sendUp(msg);
}

void FlowMultiplexer::handleSelfMsg(cMessage* msg){
    opp_error("No self message. Check configuration.");
}

void FlowMultiplexer::handleUpperMsg(cMessage *appl, int appIndex)
{
    assert(dynamic_cast<cPacket*>(appl));
    //sendDown(encapsMsg(static_cast<cPacket*>(mac)));
    sendDown(static_cast<cPacket*>(appl));
}

void FlowMultiplexer::handleUpperControl(cMessage* msg, int appIndex){
    delete(msg);
}

void FlowMultiplexer::handleLowerControl(cMessage* msg, int nicIndex) {
    sendControlUp(msg);
}

void FlowMultiplexer::sendControlUp(cMessage *msg) {
    //recordPacket(PassedMessage::OUTGOING,PassedMessage::UPPER_CONTROL,msg);
    if (gate(toControlUpLayer[0])->isPathOK())
        send(msg, toControlUpLayer[0]);
    else {
        EV << "BaseLayer: upperControlOut is not connected; dropping message" << std::endl;
        delete msg;
    }
}

void FlowMultiplexer::sendDown(cMessage *msg) {
    //recordPacket(PassedMessage::OUTGOING,PassedMessage::LOWER_DATA,msg);

    // per ora invio tutto al NIC 0
    //send(msg, toDownLayer[0]);

    // send random to down
    int nicDest;

    if (nicToUse < 0) {
        nicDest = intrand(lowLayerNumber);
    }
    else {
        nicDest = nicToUse;
    }
    send(msg, toDownLayer[nicDest]);
}

void FlowMultiplexer::sendUp(cMessage *msg) {
    int appDest;

    appDest = intrand(upLayerNumber);

    send(msg, toUpLayer[appDest]);

}
